﻿using System;
using System.Drawing;
using System.Collections.Generic;
using Cartography.Desktop;
using System.Windows.Forms;
using PointOfInterest.Desktop;
using System.Xml;
using Meteo.Desktop;
using MCDS;
using System.Drawing.Drawing2D;

namespace Cartography.Desktop
{
    public class Map : Panel
    {
        private Bitmap[,] tiles;  //Matriz de imágenes (tiles) a dibujar en la pantalla
        private Bitmap buffer;
        private int clickX, clickY,displacementZoom, displacementTileX, displacementTileY, 
            nImages, halfSize, imageSize, width, height, actualRow, actualColumn, offsetX, 
            offsetY, actualZoom, zoomLevels, aroundTile, acumuladorX, acumuladorY;
        private LatLon ne, sw,actualCenter;
        private Cache cache;
        private Graphics canvas;
        private Font letters;
        private double[] LatHeigthLonWidth;
        private string pathData;
        private List<Group> groups;
        private List<BuildingsGroup> buildingsGroups;
        private int[] centralTilePixel;
        private Panel composite;
        private PictureBox image;
        private TrackBar zoomer;
        private Boolean ImagenSatelital = false;

        //------------------------------------------
        private Boolean dibujar;
        private classEdificiosSantiago poligono;

        private System.Drawing.Point p1, p2;
        private LatLon Pto1 = new LatLon();
        private LatLon Pto2 = new LatLon();
        private System.Drawing.Point desplazamiento = new System.Drawing.Point();
        private System.Drawing.Point desplazamientoI = new System.Drawing.Point();
        //------------------------------------------
        // Meteo stuff
        private static MeteoData metdata = null;
        private Panel metPan;
        private Label temp;
        private Label hum;
        private Label cond;
        private Label wind;

        // ALDO

        private Boolean visible;
        private LatLon dest;

        // DIEGO
        private bool setteado = false;
        private LatLon pos;
        private LatLon pos_pas;

        // Cantidad de recursos más cercanos a resaltar
        private static int grifCerc = 5;


        /// <summary>
        /// Constructor of the Map class
        /// </summary>
        /// <param name="cornerNE">The NorthEast coordinates corner of the 
        /// map at zoom 0
        /// </param>
        /// <param name="cornerSW">The SouthWest coordinates corner of the 
        /// map at zoom 0
        /// </param>
        /// <param name="zoomAvaible">The zoom levels that has this map</param>
        /// <param name="mapWidth">The width of the Image constructed by this map</param>
        /// <param name="mapHeigth">The height of the Image constructed by this map</param>
        /// <param name="tilesFolder">The path to the folder that contains the info of the map, one 
        /// folder per row of the tiles, a folder per zoom
        /// </param>
        /// <param name="mapHeigth">The size of a side of the tile (square tile)</param>
        public Map(LatLon cornerNE, LatLon cornerSW, int zoomAvaible, int mapWidth, int mapHeigth, string tilesFolder, int tilesSize, int displacementInitialZoom, int displacementInitialTileX, int displacementInitialTileY)
        {
            //Inicializa Panel principal, contenedor del mapa
            this.composite = new Panel();
            this.composite.Location = new System.Drawing.Point(0,25);
            this.composite.Size = new Size(mapWidth,mapHeigth);
            this.composite.BorderStyle = BorderStyle.FixedSingle;
            this.dibujar = false;

            // Inicializa barra de la izquierda
            this.zoomer = new TrackBar();
            this.zoomer.Minimum = 0;
            this.zoomer.Maximum = zoomAvaible;
            // Inicializa componentes de temperatura
            this.metPan = new Panel();
            this.temp = new Label();
            this.hum = new Label();
            this.cond = new Label();
            this.wind = new Label();

            if (Map.metdata != null)
            {
                this.setMeteoData(Map.metdata);
            }
            else
            {
                this.temp.Text = "Temperatura:";
                this.hum.Text = "Humedad:";
                this.cond.Text = "Condición:";
                this.wind.Text = "Viento:";
            }

            this.temp.Location = new System.Drawing.Point(5, 5);
            this.hum.Location = new System.Drawing.Point(145, 5);
            this.cond.Location = new System.Drawing.Point(285, 5);
            this.wind.Location = new System.Drawing.Point(425, 5);

            this.temp.Width = 140;
            this.hum.Width = 140;
            this.cond.Width = 140;
            this.wind.Width = 140;

            // Agregar los Labels a un panel
            this.metPan.Height = 30;
            this.metPan.Width = mapWidth - 40;
            this.metPan.Location = new System.Drawing.Point(40, 0);
            this.metPan.Controls.Add(temp);
            this.metPan.Controls.Add(hum);
            this.metPan.Controls.Add(cond);
            this.metPan.Controls.Add(wind);

            //this.composite.DoubleClick += new System.EventHandler(this.zoomClick);
           

            this.image=new PictureBox();
            this.image.Size = new Size(mapWidth,mapHeigth);
            this.groups = new List<Group>(12);
            this.buildingsGroups = new List<BuildingsGroup>(2);

            this.displacementZoom = displacementInitialZoom;
            this.displacementTileX = displacementInitialTileX;
            this.displacementTileY = displacementInitialTileY;
            this.imageSize = tilesSize;
            this.offsetX = this.offsetY = 0;
            this.width = mapWidth;
            this.height = mapHeigth;
            this.halfSize = (int)Math.Floor((double)Math.Max(this.width, this.height) / 2);
            int tmpRatio = (int)Math.Ceiling((double)halfSize *3/ imageSize);
            this.nImages = (tmpRatio % 2) == 0 ? (tmpRatio + 1) : tmpRatio;
            this.actualZoom = 0;

            //Propiedades del zoomer
            this.zoomer.Size = new Size(10, this.height);
            this.zoomer.Orientation = Orientation.Vertical;
            this.zoomer.Location = new System.Drawing.Point(0,0);
            this.zoomer.TickStyle = TickStyle.TopLeft;

            //Definir el buffer (la imagen actual) y asignarla al contenedor de imagen
            this.buffer = new Bitmap(this.width, this.height);
            this.image.Image= this.buffer;

            //eventos de la imagen

            this.image.DoubleClick += new EventHandler(this.zoomClick);


            this.image.MouseMove += new MouseEventHandler(this.mouseMove);
            this.image.MouseDown += new MouseEventHandler(this.mouseDown);
            this.image.MouseUp+= new MouseEventHandler(this.mouseUp);
            //g2 = (Image)buffer.
            //cargar configuracion del mapa            
            this.ne = cornerNE;
            this.sw = cornerSW;
            this.actualCenter = new LatLon((ne.Latitude + sw.Latitude) / 2, (ne.Longitude + sw.Longitude) / 2);
            //this.actualCenter = new LatLon(ne.Latitude, sw.Longitude);
            this.zoomLevels = zoomAvaible;
            this.actualZoom=0;
            //directorio de recursos
            this.pathData = tilesFolder;
            this.poligono = new classEdificiosSantiago(pathData, this.actualZoom);
            //cambiar por pathTiles!!!
            //Acá se debería cargar la imagen de "nula"
            //Actualmente se esta rellenando la imagen con pixeles transparentes
            Bitmap noImage = new Bitmap(tilesSize, tilesSize);
            for (int i = 0; i < tilesSize;i++ )
                for (int j = 0; j < tilesSize; j++)
                    noImage.SetPixel(i,j,Color.Transparent);
            //En esta parte parece q hay q cambiar las tles
            if (FormPrincipal.Instance.cbImagenSatelital_Enable && FormPrincipal.Instance.cbImagenSatelital_Checked)
            {
                this.cache = new Cache(this.pathData + "\\tiles raster", noImage, (nImages * nImages * 2));
                this.ImagenSatelital = true;
            }
            else 
            {
                this.cache = new Cache(this.pathData + "\\tiles", noImage, (nImages * nImages * 2));
                this.ImagenSatelital = false;
            }
            
            this.tiles = new Bitmap[nImages, nImages];
            this.aroundTile = (int)Math.Floor((double)(nImages / 2));
            this.letters = new Font(FontFamily.GenericMonospace, 12, FontStyle.Regular);
            //Agregar los grifos a la lista de grupos de puntos de interes
            Console.Write(this.pathData + "\n");
            //string path = this.pathData + "\\PointsOfInterest\\Grifos.xml";

            //************************************* Capas de Chile*************************************

            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_La_Florida.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_La_Reina.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_Macul.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_Nunoa.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_Penalolen.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Carabineros.xml", this.pathData + "\\Icons", FormPrincipal.Instance.capaCarabineros));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Hospital.xml", this.pathData + "\\Icons", FormPrincipal.Instance.capaHospitales));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Bomberos.xml", this.pathData + "\\Icons", FormPrincipal.Instance.capaCuartelesBomberos));
            //************************************* Fin Capas de Chile*************************************


            if (System.IO.File.Exists(this.pathData + "\\PointsOfInterest\\CarrosEmergencia.xml"))
                this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\CarrosEmergencia.xml", this.pathData + "\\Icons", true));

            if (System.IO.File.Exists(this.pathData + "\\PointsOfInterest\\Emergencia.xml"))
                this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Emergencia.xml", this.pathData + "\\Icons", true));
            
            this.center(this.actualCenter);
            if (System.IO.File.Exists(this.pathData + "\\PointsOfInterest\\EdificioEmergencia.xml")) 
            {
                BuildingsGroup Edificio = new BuildingsGroup(this.pathData + "\\PointsOfInterest\\EdificioEmergencia.xml", true);
                this.buildingsGroups.Add(Edificio);
                
            }
            this.buildingsGroups.Add(new BuildingsGroup(this.pathData + "\\PointsOfInterest\\Edificios.xml", true));

            

            //LLamada a refresh, para pintar la imagen
            this.refresh();
            this.composite.BackColor = Color.Transparent;
            this.image.BackColor = Color.Transparent;

            //Añadir handler al zoomer
            this.zoomer.ValueChanged += new System.EventHandler(this.updateZoom);

            //Agregar al panel principal el zoomer y la imagen
            this.composite.Controls.Add(this.zoomer);
            this.composite.Controls.Add(this.metPan);
            this.composite.Controls.Add(this.image);
        }

        public Map(int zoomInicial,LatLon coordenadasInicial,LatLon cornerNE, LatLon cornerSW, int zoomAvaible, int mapWidth, int mapHeigth, string tilesFolder, int tilesSize, int displacementInitialZoom, int displacementInitialTileX, int displacementInitialTileY)
        {
            this.composite = new Panel();
            this.composite.Location = new System.Drawing.Point(0, 25);
            this.composite.Size = new Size(mapWidth, mapHeigth);
            this.composite.BorderStyle = BorderStyle.FixedSingle;
            this.dibujar = false;

            this.zoomer = new TrackBar();
            this.zoomer.Minimum = 0;
            this.zoomer.Maximum = zoomAvaible;

            //this.composite.DoubleClick += new System.EventHandler(this.zoomClick);

            // Inicializa componentes de temperatura
            this.metPan = new Panel();
            this.temp = new Label();
            this.hum = new Label();
            this.cond = new Label();
            this.wind = new Label();

            if (Map.metdata != null)
            {
                this.setMeteoData(Map.metdata);
            }
            else
            {
                this.temp.Text = "Temperatura:";
                this.hum.Text = "Humedad:";
                this.cond.Text = "Condición:";
                this.wind.Text = "Viento:";
            }

            this.temp.Location = new System.Drawing.Point(5, 5);
            this.hum.Location = new System.Drawing.Point(145, 5);
            this.cond.Location = new System.Drawing.Point(285, 5);
            this.wind.Location = new System.Drawing.Point(425, 5);

            this.temp.Width = 140;
            this.hum.Width = 140;
            this.cond.Width = 140;
            this.wind.Width = 140;

            // Agregar los Labels a un panel
            this.metPan.Height = 30;
            this.metPan.Width = mapWidth - 40;
            this.metPan.Location = new System.Drawing.Point(40, 0);
            this.metPan.Controls.Add(temp);
            this.metPan.Controls.Add(hum);
            this.metPan.Controls.Add(cond);
            this.metPan.Controls.Add(wind);

            


           
            this.image = new PictureBox();
            this.image.Size = new Size(mapWidth, mapHeigth);
            this.groups = new List<Group>(12);
            this.buildingsGroups = new List<BuildingsGroup>(2);
            this.displacementZoom = displacementInitialZoom;
            this.displacementTileX = displacementInitialTileX;
            this.displacementTileY = displacementInitialTileY;
            this.imageSize = tilesSize;
            this.offsetX = this.offsetY = 0;
            this.width = mapWidth;
            this.height = mapHeigth;
            this.halfSize = (int)Math.Floor((double)Math.Max(this.width, this.height) / 2);
            int tmpRatio = (int)Math.Ceiling((double)halfSize * 3 / imageSize);
            this.nImages = (tmpRatio % 2) == 0 ? (tmpRatio + 1) : tmpRatio;
            this.actualZoom = zoomInicial;
            this.zoomer.Size = new Size(10, this.height);
            this.zoomer.Orientation = Orientation.Vertical;
            this.zoomer.Location = new System.Drawing.Point(0, 0);
            this.zoomer.TickStyle = TickStyle.TopLeft;
            this.zoomer.Value = actualZoom;
            if (this.width == 0 && this.height == 0) return;
            this.buffer = new Bitmap(this.width, this.height);
            this.image.Image = this.buffer;

            //eventos de la imagen

            this.image.DoubleClick += new EventHandler(this.zoomClick);
            this.image.MouseMove += new MouseEventHandler(this.mouseMove);
            this.image.MouseDown += new MouseEventHandler(this.mouseDown);
            this.image.MouseUp += new MouseEventHandler(this.mouseUp);
            //g2 = (Image)buffer.
            //cargar configuracion del mapa            
            this.ne = cornerNE;
            this.sw = cornerSW;
            this.actualCenter = coordenadasInicial;
            //this.actualCenter = new LatLon(ne.Latitude, sw.Longitude);
            this.zoomLevels = zoomAvaible;
            this.actualZoom = zoomInicial;
            //directorio de recursos
            this.pathData = tilesFolder;
            this.poligono = new classEdificiosSantiago(pathData, this.actualZoom);
            //cambiar por pathTiles!!!
            Bitmap noImage = new Bitmap(tilesSize, tilesSize);
            for (int i = 0; i < tilesSize; i++)
                for (int j = 0; j < tilesSize; j++)
                    noImage.SetPixel(i, j, Color.Transparent);
            if (FormPrincipal.Instance.cbImagenSatelital_Enable && FormPrincipal.Instance.cbImagenSatelital_Checked)
            {
                this.cache = new Cache(this.pathData + "\\tiles raster", noImage, (nImages * nImages * 2));
                this.ImagenSatelital = true;
            }
            else
            {
                this.cache = new Cache(this.pathData + "\\tiles", noImage, (nImages * nImages * 2));
                this.ImagenSatelital = false;
            }
            this.tiles = new Bitmap[nImages, nImages];
            this.aroundTile = (int)Math.Floor((double)(nImages / 2));
            this.letters = new Font(FontFamily.GenericMonospace, 12, FontStyle.Regular);

            //************************************* Capas de Chile*************************************
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_La_Florida.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_La_Reina.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_Macul.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_Nunoa.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_Penalolen.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Carabineros.xml", this.pathData + "\\Icons", FormPrincipal.Instance.capaCarabineros));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Hospital.xml", this.pathData + "\\Icons", FormPrincipal.Instance.capaHospitales));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Bomberos.xml", this.pathData + "\\Icons", FormPrincipal.Instance.capaCuartelesBomberos));
            //************************************* Fin Capas de Chile*************************************
            if (System.IO.File.Exists(this.pathData + "\\PointsOfInterest\\Emergencia.xml"))
                this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Emergencia.xml", this.pathData + "\\Icons", true));

            this.center(this.actualCenter);
            if (System.IO.File.Exists(this.pathData + "\\PointsOfInterest\\EdificioEmergencia.xml"))
            {
                BuildingsGroup Edificio = new BuildingsGroup(this.pathData + "\\PointsOfInterest\\EdificioEmergencia.xml", true);
                this.buildingsGroups.Add(Edificio);
            }
            this.buildingsGroups.Add(new BuildingsGroup(this.pathData + "\\PointsOfInterest\\Edificios.xml", true));
            
            
            
            this.refresh();
            this.composite.BackColor = Color.Transparent;
            this.image.BackColor = Color.Transparent;

            this.zoomer.ValueChanged += new System.EventHandler(this.updateZoom);
            this.composite.Controls.Add(this.zoomer);
            this.composite.Controls.Add(this.metPan);
            this.composite.Controls.Add(this.image);
            this.refresh();
        }
        private void dibujarPuntos() 
        {
            foreach (Group group in groups)
            {
                
                //&& this.actualZoom >= group.Zoom
                if (group.Visible && this.actualZoom >= group.Zoom)
                {
                    foreach (PointOfInterest.Desktop.Point point in group.visiblePoints)
                    {
                        if (point.actualZoom != this.actualZoom)
                        {
                            int[] position = this.toZoomedPixelCoords(point.Coordenadas, this.actualZoom);
                            point.actualPixelX = position[0];
                            point.actualPixelY = position[1];
                            point.actualZoom = actualZoom;
                        }
                        int positionScreenX = this.width / 2 + (point.actualPixelX - centralTilePixel[0]) - 16;
                        int positionScreenY = this.height / 2 + (point.actualPixelY - centralTilePixel[1]) - 16;
                        if (positionScreenX > this.width || positionScreenX < -32) continue;
                        if (positionScreenY > this.height || positionScreenY < -32) continue;
                        point.Location = new System.Drawing.Point(positionScreenX, positionScreenY);
                        if (point.Name == "Emergencia") point.BringToFront();

                        this.image.Controls.Add(point);
                        point.estaSeleccionado();
                        //JAJAJAJAJ. Descomentar esta línea
                        //(Y generar un xml con los puntos de interes)
                        // Para que se muestren los puntos de interes en el mapa
                        //Dibujar el punto en el mapa
                        this.canvas.DrawImage(group.Icon, positionScreenX, positionScreenY);
                        //canvas.DrawImage(group.Icon, positionScreenX, positionScreenY);
                    }
                }
            }
        }
        private void dibujarDebug() 
        {
            //this.composite.Controls.Remove(this.image);
            //this.composite.Controls.Add(image);
            //int[] pixD = this.dGeo2dPix(-33.45399, -70.58769, this.actualZoom);
            //Bitmap icono = new Bitmap(this.pathData+"/iconos/casa32.png");
            //Bitmap hydrant = new Bitmap(this.pathData + "/iconos/hydrant.png");
            int[] pixD = this.toZoomedPixelCoords(new LatLon(-33.454270276086284, -70.58790922164917), this.actualZoom);
            int[] pixGrifo = this.toZoomedPixelCoords(new LatLon(-33.454270276086284, -70.58790922164917), this.actualZoom);
            int[] pixD2 = this.dGeo2dPix(-33.454270276086284, -70.58790922164917, this.actualZoom);

            canvas.DrawString("offSetX=" + offsetX + " offSetY=" + offsetY, this.letters, new SolidBrush(Color.Red), new RectangleF(20, 20, 300, 20));
            canvas.DrawString("actualRow=" + actualRow + " actualColumn=" + actualColumn, this.letters, new SolidBrush(Color.Red), new RectangleF(20, 50, 300, 20));
            canvas.DrawString("width=" + this.width + " height=" + this.height, this.letters, new SolidBrush(Color.Red), new RectangleF(20, 80, 300, 20));
            canvas.DrawString("zoom=" + this.actualZoom + " geo pix: X" + (centralTilePixel[0]) + " Y" + (centralTilePixel[1]), this.letters, new SolidBrush(Color.Red), new RectangleF(20, 110, 300, 20));
            //canvas.DrawString("Lat center="+this.actualCenter.Latitude, this.letters, new SolidBrush(Color.Red), new RectangleF(20, 130, 300, 20));
            //canvas.DrawString("Lon center=" + this.actualCenter.Longitude, this.letters, new SolidBrush(Color.Red), new RectangleF(20, 150, 300, 20));
            //buffer.SetPixel(pixD[1], pixD[0], Color.Salmon);
            canvas.DrawString("C", this.letters, new SolidBrush(Color.Black), new RectangleF((centralTilePixel[0] + offsetX), (centralTilePixel[1] + offsetY), 20, 20)); // this.width - imageSize/2 - 20  por this.width - imageSize/2 + 100
            canvas.DrawString("O", this.letters, new SolidBrush(Color.Black), new RectangleF(offsetX, offsetY, 20, 20)); // this.width - imageSize/2 - 20  por this.width - imageSize/2 + 100
            canvas.DrawString("A", this.letters, new SolidBrush(Color.Black), new RectangleF(acumuladorX, acumuladorY, 20, 20)); // this.width - imageSize/2 - 20  por this.width - imageSize/2 + 100
            //canvas.DrawString("M", this.letters, new SolidBrush(Color.Black), new RectangleF(pixD[0]-offsetX , pixD[1]-offsetY , 30, 30)); // this.width - imageSize/2 - 20  por this.width - imageSize/2 + 100
            //canvas.DrawString("H", this.letters, new SolidBrush(Color.Black), new RectangleF(this.width / 2 + (pixD[0] - centralTilePixel[0]), this.height / 2 + (pixD[1] - centralTilePixel[1]), 30, 30)); // this.width - imageSize/2 - 20  por this.width - imageSize/2 + 100
            //canvas.DrawImage(icono, this.width / 2 + (pixD[0] - centralTilePixel[0])-16, this.height / 2 + (pixD[1] - centralTilePixel[1])-16);
            //canvas.DrawImage(hydrant, this.width / 2 + (pixGrifo[0] - centralTilePixel[0]) - 16, this.height / 2 + (pixGrifo[1] - centralTilePixel[1]) - 16);
            //canvas.DrawImage(icono, pixD2[1] - 16, pixD2[0]- 16);
        }
        private void dibujarPoligono() 
        {
            if (this.poligono.NumeroDePuntos > 1)
            {
                //MessageBox.Show("Entro");
                int[] punto1, punto2;
                int ultimo = 0;
                if (!this.poligono.Completo)
                {
                    for (int i = 1; i < this.poligono.NumeroDePuntos; i++)
                    {
                        punto1 = this.toZoomedPixelCoords(this.poligono.Coordenadas[ultimo], this.actualZoom);
                        punto2 = this.toZoomedPixelCoords(this.poligono.Coordenadas[i], this.actualZoom);
                        int positionScreenX = this.width / 2 + (punto1[0] - centralTilePixel[0]);
                        int positionScreenY = this.height / 2 + (punto1[1] - centralTilePixel[1]);
                        punto1[0] = positionScreenX;
                        punto1[1] = positionScreenY;
                        positionScreenX = this.width / 2 + (punto2[0] - centralTilePixel[0]);
                        positionScreenY = this.height / 2 + (punto2[1] - centralTilePixel[1]);
                        punto2[0] = positionScreenX;
                        punto2[1] = positionScreenY;
                        canvas.DrawLine(new System.Drawing.Pen(System.Drawing.Color.DarkOrange, 2), new System.Drawing.Point(punto1[0], punto1[1]), new System.Drawing.Point(punto2[0], punto2[1]));

                        ultimo = i;
                    }
                }
                else
                {

                    this.poligono = new classEdificiosSantiago(this.pathData, this.actualZoom);
                    this.buildingsGroups = new List<BuildingsGroup>(2);
                    this.buildingsGroups.Add(new BuildingsGroup(this.pathData + "\\PointsOfInterest\\Edificios.xml", true));
                }
            }
            this.dibujarPoligono2();
        }
        //public void puntosDeInteresEdificio(string grupo, Map mapa)
        //{
        //    //if (canvas != null)
        //    //{
        //    //    //Nodos de los puntos de interes
        //    //    XmlDocument doc = new XmlDocument();
        //    //    System.Globalization.NumberFormatInfo nfi = new System.Globalization.CultureInfo("en-US", false).NumberFormat;
        //    //    doc.Load(pathData + "\\PointsOfInterest\\" + grupo + ".xml");
        //    //    XmlNode redSeca = doc.SelectSingleNode("PointsOfInterest/Edificios/Edificio/RedSeca");
        //    //    XmlNode redInerte = doc.SelectSingleNode("PointsOfInterest/Edificios/Edificio/RedInerte");
        //    //    XmlNode viaEvacuacion = doc.SelectSingleNode("PointsOfInterest/Edificios/Edificio/ViaEvacuacion");
        //    //    //Iconos de los puntods de interes
        //    //    System.Drawing.Bitmap tmpDibujar = new System.Drawing.Bitmap(pathData + "\\Icons\\redElectricaInerte.png");
        //    //    System.Drawing.Bitmap iconRedInerte = new System.Drawing.Bitmap(tmpDibujar);
        //    //    System.Drawing.Bitmap iconRedSeca = new System.Drawing.Bitmap(new System.Drawing.Bitmap(pathData + "\\Icons\\redSecaEntrada.png"));
        //    //    System.Drawing.Bitmap iconViaEvacuacion = new System.Drawing.Bitmap(new System.Drawing.Bitmap(pathData + "\\Icons\\viaEvacuacion.png"));

        //    //    string Latitud, Longitud;
        //    //    int[] punto;
        //    //    int positionScreenX;
        //    //    int positionScreenY;
        //    //    if ((redSeca.SelectSingleNode("Latitud").InnerText != null) && (redSeca.SelectSingleNode("Longitud").InnerText != null))
        //    //    {
        //    //        Latitud = redSeca.SelectSingleNode("Latitud").InnerText;
        //    //        Longitud = redSeca.SelectSingleNode("Longitud").InnerText;
        //    //        punto = this.toZoomedPixelCoords(new LatLon(Latitud, Longitud), this.getActualZoom);
        //    //        positionScreenX = this.getWidth / 2 + (punto[0] - this.CentralTilePixel[0]);
        //    //        positionScreenY = this.getHeight / 2 + (punto[1] - this.CentralTilePixel[1]);
        //    //        System.Windows.Forms.PictureBox tmpPB = new System.Windows.Forms.PictureBox();
        //    //        tmpPB.Image = iconRedSeca;
        //    //        tmpPB.Location = new System.Drawing.Point(positionScreenX, positionScreenY);
        //    //        tmpPB.BringToFront();
        //    //        //this.image.Controls.Add(tmpPB);
        //    //        canvas.DrawImage(iconRedSeca, positionScreenX, positionScreenY);

        //    //    }
        //    //}
            
        //}
        public void actualizarPuntosDeInteres() 
        {
            this.groups = new List<Group>(12);
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_La_Florida.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_La_Reina.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_Macul.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_Nunoa.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Grifos_Penalolen.xml", this.pathData + "\\Icons", true));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Carabineros.xml", this.pathData + "\\Icons", false));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Hospital.xml", this.pathData + "\\Icons", false));
            this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Bomberos.xml", this.pathData + "\\Icons", false));

            if (System.IO.File.Exists(this.pathData + "\\PointsOfInterest\\CarrosEmergencia.xml"))
                this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\CarrosEmergencia.xml", this.pathData + "\\Icons", true));

            if (System.IO.File.Exists(this.pathData + "\\PointsOfInterest\\Emergencia.xml"))
                this.groups.Add(new Group(this.pathData + "\\PointsOfInterest\\Emergencia.xml", this.pathData + "\\Icons", true));


            LatLon cornerNW = new LatLon(this.actualCenter.Latitude + this.LatHeigthLonWidth[0] * aroundTile, this.actualCenter.Longitude - this.LatHeigthLonWidth[1] * aroundTile);
            LatLon cornerSE = new LatLon(this.actualCenter.Latitude - this.LatHeigthLonWidth[0] * aroundTile, this.actualCenter.Longitude + this.LatHeigthLonWidth[1] * aroundTile);
            foreach (Group group in groups)
            {
                if (group.Visible && this.actualZoom >= group.Zoom)
                {
                    group.update(cornerNW, cornerSE);
                }
            }
        }
        public void actualizarGrupoEdificios()
        {
            this.buildingsGroups = new List<BuildingsGroup>(2);
            if (System.IO.File.Exists(this.pathData + "\\PointsOfInterest\\EdificioEmergencia.xml"))
            {
                this.buildingsGroups.Remove(new BuildingsGroup(this.pathData + "\\PointsOfInterest\\EdificioEmergencia.xml", true));
                BuildingsGroup Edificio = new BuildingsGroup(this.pathData + "\\PointsOfInterest\\EdificioEmergencia.xml", true);
                this.buildingsGroups.Add(Edificio);
            }

            this.buildingsGroups.Add(new BuildingsGroup(this.pathData + "\\PointsOfInterest\\Edificios.xml", true));
        }
        private void dibujarPoligono2() 
        {
            LatLon cornerNW = new LatLon(this.actualCenter.Latitude + this.LatHeigthLonWidth[0] * aroundTile, this.actualCenter.Longitude - this.LatHeigthLonWidth[1] * aroundTile);
            LatLon cornerSE = new LatLon(this.actualCenter.Latitude - this.LatHeigthLonWidth[0] * aroundTile, this.actualCenter.Longitude + this.LatHeigthLonWidth[1] * aroundTile);

            foreach (BuildingsGroup grupoEdificios in buildingsGroups)
            {
                if (grupoEdificios.Visible && this.actualZoom >= grupoEdificios.Zoom)
                {
                    grupoEdificios.update(cornerNW, cornerSE);
                    foreach (classEdificiosSantiago tmpEdificio in grupoEdificios.visiblePoints)
                    {
                        if (grupoEdificios.Name == "EdificioEmergencia")
                        {
                            tmpEdificio.dibujarPoligono(this, "Emergencia");
                            //tmpEdificio.BringToFront();
                            tmpEdificio.puntosDeInteresEdificio("EdificioEmergencia", this);
                            this.image.Controls.Add(tmpEdificio);
                            tmpEdificio.dibujarAreaDeRiesgo();
                            
                        }
                        else 
                        {
                            tmpEdificio.dibujarPoligono(this, "Default");
                            this.image.Controls.Add(tmpEdificio);
                        }
                        

                        

                    }
                }
            }
        }

        public void refresh()
        {
            
            this.image.Controls.Clear();
            canvas = Graphics.FromImage(buffer);
            canvas.Clear(Color.White);

            for (int i = 0; i < nImages; i++)
            {
                for (int j = 0; j < nImages; j++)
                {
                    int tx = offsetX + (j - aroundTile) * imageSize;
                    int ty = offsetY + (i - aroundTile) * imageSize;
                    if (tx > this.width || tx < -imageSize) continue;
                    if (ty > this.height || ty < -imageSize) continue;
                    if (tiles[i, j] == null) continue;
                    canvas.DrawImage(tiles[i, j], tx, ty);
                    //canvas.DrawString("[" + i + "," + j + "]", this.letters, new SolidBrush(Color.Red), new RectangleF(tx, ty, 50, 20));
                }
            }
            this.dibujarPuntos();
            //this.dibujarDebug();
            this.dibujarDistancia();
            this.dibujarPoligono();

            //grupo Ing. de Software

            // Dibujar destino en el mapa
            this.dibujarDest(dest);
            // Dibujar la posicion en el mapa
            if (this.setteado)
            {
                if (pos != null)
                {
                    this.dibujarPosicion(pos);
                    // Dibujar flechas en el mapa
                    this.drawArrows();
                }
            }
            
            canvas.DrawImage(buffer, 0, 0);
            canvas.Dispose();
            this.image.Refresh();
        }
         private void drawArrows()
        {
            // Chequear si alguno de los 2 puntos es null
            
            int[] p1 = new int[2];
            int[] p2 = new int[2];
            double theta;
            p2 = this.toZoomedPixelCoords(this.pos, this.actualZoom);
            p2[0] = this.width / 2 + (p2[0] - centralTilePixel[0]);
            p2[1] = this.height / 2 + (p2[1] - centralTilePixel[1]);
            if (this.pos_pas != null)
            {

                // Calcular las 2 posiciones en pixeles
                

                p1 = this.toZoomedPixelCoords(this.pos_pas, this.actualZoom);
               

                p1[0] = this.width / 2 + (p1[0] - centralTilePixel[0]);
                p1[1] = this.height / 2 + (p1[1] - centralTilePixel[1]);



                // Calcular el ángulo de la flecha.
                theta = Math.Atan(((double)(p2[1] - p1[1])) / ((double)(p2[0] - p1[0])));

                if (Double.IsNaN(theta))
                {
                    return;
                }

                // Dibujar flecha para apuntar hacia donde va
                this.drawArrow(p1[0], p1[1], theta, 50, Color.Blue);
            }
            LatLon coords = new LatLon();

             //obtengo las coordenadas ya sean del archivo de emergencia o del archivo de edificio de emergencia
            XmlDocument doc;
            System.Globalization.NumberFormatInfo nfi = new System.Globalization.CultureInfo("en-US", false).NumberFormat;
            if (System.IO.File.Exists(this.pathData + "\\PointsOfInterest\\Emergencia.xml"))
            {
                doc = new XmlDocument();
                doc.Load(this.pathData + "\\PointsOfInterest\\Emergencia.xml");
                coords.Latitude = double.Parse(doc.SelectSingleNode("PointsOfInterest/Points/Point/Coordinates/Latitude").InnerText, nfi);
                coords.Longitude = double.Parse(doc.SelectSingleNode("PointsOfInterest/Points/Point/Coordinates/Longitude").InnerText, nfi);
            }
            else
            {
                if (System.IO.File.Exists(this.pathData + "\\PointsOfInterest\\EdificioEmergencia.xml"))
                {
                    doc = new XmlDocument();
                    doc.Load(this.pathData + "\\PointsOfInterest\\EdificioEmergencia.xml");
                    coords.Latitude = double.Parse(doc.SelectSingleNode("PointsOfInterest/Edificios/Edificio/Centroide/Latitud").InnerText, nfi);
                    coords.Longitude = double.Parse(doc.SelectSingleNode("PointsOfInterest/Edificios/Edificio/Centroide/Longitud").InnerText, nfi);
                }
                else 
                {
                    return;
                }
            }
            if (coords.Latitude != 0 && coords.Longitude!=0)
            {
                p1 = this.toZoomedPixelCoords(coords, this.actualZoom);

                p1[0] = this.width / 2 + (p1[0] - centralTilePixel[0]);
                p1[1] = this.height / 2 + (p1[1] - centralTilePixel[1]);

                // Calcular el ángulo de la flecha.
                theta = Math.Atan(((double)(p2[1] - p1[1])) / ((double)(p2[0] - p1[0])));

                if (Double.IsNaN(theta))
                {
                    return;
                }

                // Dibujar flecha para apuntar hacia donde va
                this.drawArrow(p2[0], p2[1], theta, 50, Color.Red);
            }

        }
        private void drawArrow(int px, int py, double theta, double size, Color col)
        {
            // Calcular pto2
            int y = (int) (py + size * Math.Sin(theta));
            int x = (int) (px + size * Math.Cos(theta));

            System.Drawing.Point punto1 = new System.Drawing.Point(px, py - 8);

            System.Drawing.Point punto2 = new System.Drawing.Point(x, y - 8);

            Color color = Color.FromArgb(150, col);
            Pen p = new Pen(color, 10);
            p.StartCap = LineCap.Round;
            p.EndCap = LineCap.ArrowAnchor;
            canvas.DrawLine(p, punto1, punto2);
        }
    
        /// <summary>
        /// Move the map to a new point in pixels
        /// </summary>
        /// <param name="x">The amount of pixels that should move on the X axis the sign indicate the direction</param>
        /// <param name="y">The amount of pixels that should move on the Y axis the sign indicate the direction</param>
        public string dPix2dKM(System.Drawing.Point p1, System.Drawing.Point p2)
        {
            double anchoKM = 73;
            int dpix = (int)Math.Sqrt(Math.Pow((p1.X - p2.X), 2) + Math.Pow((p1.Y - p2.Y), 2));
            double anchoKmActual = anchoKM / Math.Pow(2, this.actualZoom);
            double the_dist = (dpix * anchoKmActual) / imageSize;
            if (the_dist < 1)
            {
                the_dist *= 1000;
                return the_dist.ToString("N") + " m.";
            }
            return the_dist.ToString("N") + " Km.";
        }

        public double distanciaMts(LatLon p1,LatLon p2) 
        {
	        double R = 6371000; // mts 
	        double dLat = (p2.Latitude-p1.Latitude) * Math.PI / 180;
	        double dLon = (p2.Longitude-p1.Longitude) * Math.PI / 180;
	        double a = Math.Sin(dLat/2) * Math.Sin(dLat/2) +
	            Math.Cos(p1.Latitude* Math.PI / 180 ) * Math.Cos(p1.Latitude * Math.PI / 180 ) *
	            Math.Sin(dLon/2) * Math.Sin(dLon/2);
	        double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1-a));
	        double d = R * c;
            //if (d>1) return Math.round(d)+"km";
            //else if (d<=1) return Math.round(d*1000)+"m";
	        return d;
        }
        private void dibujarDistancia() 
        {
            if (FormPrincipal.Instance.DibujarDistancia)
            {
                System.Drawing.Pen lapiz =new System.Drawing.Pen(System.Drawing.Color.Black,2);
                lapiz.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                canvas.DrawLine(lapiz, p1.X + desplazamientoI.X, p1.Y + desplazamientoI.Y, p2.X + desplazamiento.X, p2.Y + desplazamiento.Y);
                string dist = this.distanciaMts(Pto1, Pto2).ToString("#.00") +" mts";
                canvas.DrawString("dist = " + dist, new Font(FontFamily.GenericMonospace, 10, FontStyle.Bold), 
                    new SolidBrush(Color.Black),new PointF(p2.X+desplazamiento.X,(p2.Y-15)+desplazamiento.Y));
            }
        }
        public void move(int x, int y) {
                //acumuladorX += x;
                //acumuladorY += y;
                centralTilePixel[0] -=x;
                centralTilePixel[1] -=y;
                this.actualCenter.Latitude +=((this.LatHeigthLonWidth[0]/imageSize)*y); //Lat
                this.actualCenter.Longitude -= ((this.LatHeigthLonWidth[1] / imageSize) * x); //Lat
                offsetX += x;
                offsetY += y;
                bool updateTiles = false;
                int displacement, diffOffset;
                if (offsetX < 0)
                {
                    diffOffset = -offsetX + (halfSize);
                    displacement = (int)Math.Ceiling((double)(diffOffset / imageSize));
                    actualColumn += displacement;
                    offsetX += imageSize * displacement;
                    updateTiles=true;
                }
                else if (offsetX > halfSize)
                {
                    diffOffset = offsetX - (halfSize);
                    displacement = (int)Math.Ceiling((double)diffOffset / imageSize);
                    actualColumn -= displacement;
                    offsetX -= imageSize * displacement;
                    updateTiles = true;
                }

                if (offsetY < 0)
                {
                    diffOffset = -offsetY + (halfSize);
                    displacement = (int)Math.Ceiling((double)(diffOffset / imageSize));
                    actualRow += displacement;
                    offsetY += imageSize * displacement;
                    updateTiles = true;
                }
                else if (offsetY > halfSize)
                {
                    diffOffset = offsetY - (halfSize);
                    displacement = (int)Math.Ceiling((double)diffOffset / imageSize);
                    actualRow -= displacement;
                    offsetY -= imageSize * displacement;
                    updateTiles = true;
                }
            //this.actualCenter=new LatLon
                if (updateTiles)
                    updateTilesMatrix();
            //refresh();
        }
        public void zoomIn()
        {
            this.zoom(actualZoom + 1);
            this.refresh();
        }
        public void zoomOut()
        {
            this.zoom(actualZoom - 1);
            this.refresh();
        }
        public void zoom(int zoom)
        {
            if (zoom > zoomLevels || zoom < 0)
                return;
            this.actualZoom = zoom;
            this.zoomer.Value = this.actualZoom;
            this.center(this.actualCenter);
            this.move(0, 0);
            return;
        }
        private LatLon getNE(int fila, int columna, int zoom)
        {
            double salto_lat_tile = Math.Abs(ne.Latitude - sw.Latitude) / Math.Pow(2, zoom);
            double salto_long_tile = Math.Abs(ne.Longitude- sw.Longitude) / Math.Pow(2, zoom);

            LatLon tmp = new LatLon();
            tmp.Latitude = ne.Latitude - (fila - 1) * salto_lat_tile;
            tmp.Longitude = sw.Longitude + columna * salto_long_tile;
            return tmp;
        }
        private LatLon getSW(int fila, int columna, int zoom)
        {
            double salto_lat_tile = Math.Abs(ne.Latitude - sw.Latitude) / Math.Pow(2, zoom);
            double salto_long_tile = Math.Abs(ne.Longitude - sw.Longitude) / Math.Pow(2, zoom);

            LatLon tmp = new LatLon();
            tmp.Latitude = ne.Latitude - fila * salto_lat_tile;
            tmp.Longitude = sw.Longitude+ (columna - 1) * salto_long_tile;
            return tmp;
        }
        public LatLon calcularDestino(double[] puntoOrigen, double bearing, double distancia) 
        {
            System.Globalization.NumberFormatInfo nfi = new System.Globalization.CultureInfo("en-US", false).NumberFormat;
            double R = 6371000;
            double[] centroide = puntoOrigen;
            double dist = distancia / R;
            double brng = Math.PI * bearing / 180;
            double lat1 = Math.PI * centroide[1] / 180;
            double lon1 = Math.PI * centroide[0] / 180;

            double lat2 = Math.Asin(Math.Sin(lat1) * Math.Cos(dist) + Math.Cos(lat1) * Math.Sin(dist) * Math.Cos(brng));
            double lon2 = lon1 + Math.Atan2(Math.Sin(brng) * Math.Sin(dist) * Math.Cos(lat1), Math.Cos(dist) - Math.Sin(lat1) * Math.Sin(lat2));
            lon2 = (lon2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI;
            lat2 = lat2 * (180 / Math.PI);
            lon2 = lon2 * (180 / Math.PI);

            return (new LatLon(lat2, lon2));
        }
        private LatLon[] tileLatLon(int x, int y, int zoom)
        {
            x += (displacementTileX * 1 << zoom) - 1;
            y += (displacementTileY * 1 << zoom) - 1;
            zoom += displacementZoom;
            int tilesAtThisZoom = 1<<zoom;
            double lngWidth = 360.0 / tilesAtThisZoom; // width in degrees longitude
            double lng = -180 + (x * lngWidth); // left edge in degrees longitude

            double latHeightMerc = 1.0 / tilesAtThisZoom; // height in "normalized" mercator 0,0 top left
            double topLatMerc = y * latHeightMerc; // top edge in "normalized" mercator 0,0 top left
            double bottomLatMerc = topLatMerc + latHeightMerc;

            // convert top and bottom lat in mercator to degrees
            // note that in fact the coordinates go from about -85 to +85 not -90 to 90!
            double bottomLat = radian2Degree((2 * Math.Atan(Math.Exp(Math.PI * (1 - (2 * bottomLatMerc)))))
                               - (Math.PI / 2));
            double topLat = radian2Degree((2 * Math.Atan(Math.Exp(Math.PI * (1 - (2 * topLatMerc))))) - (Math.PI / 2));
            return new LatLon[2] { new LatLon(topLat, lng), new LatLon(bottomLat, lng + lngWidth) };
        }
        private double[] tileLonWidthLatHeight(int x, int y, int zoom)
        {
            x += (displacementTileX * 1 << zoom) - 1;
            y += (displacementTileY * 1 << zoom) - 1;
            zoom += displacementZoom;
            int tilesAtThisZoom = 1 << zoom;
            double lngWidth = 360.0 / tilesAtThisZoom; // width in degrees longitude
            double latHeightMerc = 1.0 / tilesAtThisZoom; // height in "normalized" mercator 0,0 top left
            double topLatMerc = y * latHeightMerc; // top edge in "normalized" mercator 0,0 top left
            double bottomLatMerc = topLatMerc + latHeightMerc;
            // convert top and bottom lat in mercator to degrees
            // note that in fact the coordinates go from about -85 to +85 not -90 to 90!
            double bottomLat = radian2Degree((2 * Math.Atan(Math.Exp(Math.PI * (1 - (2 * bottomLatMerc)))))
                               - (Math.PI / 2));
            double topLat = radian2Degree((2 * Math.Atan(Math.Exp(Math.PI * (1 - (2 * topLatMerc))))) - (Math.PI / 2));
            double latHeight = topLat - bottomLat;
            return new double[2] { latHeight, lngWidth};
        }
        public double[] toNormalisedPixelCoords(LatLon coordenadas)
        {
            
            double lat=coordenadas.Latitude;
            double lon = coordenadas.Longitude;
            // first convert to Mercator projection
            // first convert the lat lon to mercator coordintes.
            if (lon > 180)
            {
                lon-= 360;
            }

            lon /= 360;
            lon += 0.5;

            lat = 0.5 - ((Math.Log(Math.Tan((Math.PI / 4) + (degree2Radian(0.5 * lat)))) / Math.PI) / 2.0);
            // Lat eje Y Lon eje X
            return new double[2] {lon,lat};
        }
        public int[] toTileXY(LatLon coordenadas, int zoom)
        {
            zoom += displacementZoom;
            double[] normalised = toNormalisedPixelCoords(coordenadas);
            double scale = 1 << zoom;

            // can just truncate to integer, this looses the fractional "pixel offset"
            return new int[2] { (int)((normalised[0] * scale) - (displacementTileX * (1 << actualZoom)))+1 , (int)((normalised[1] * scale) - (displacementTileY * (1 << actualZoom)))+1 };
        }
        public int[] toZoomedPixelCoords(LatLon coordenadas, int zoom)
        {
            zoom += displacementZoom;
            double[] normalised = toNormalisedPixelCoords(coordenadas);
            double scale = (1 << zoom) * imageSize;
            return new int[2] { (int)((normalised[0] * scale) - (((displacementTileX * 1 << actualZoom)) * imageSize)), (int)(((normalised[1] * scale)-((displacementTileY * 1 << actualZoom)) * imageSize)) };
         }
        public void toCoordsZoomedPixel(int x, int y, int zoom)
        {
            zoom += displacementZoom;
            double scale = (1 << zoom) * imageSize;
            double normLat=x+(((displacementTileX * 1 << actualZoom)) * imageSize)/scale;
            double normLon=y+(((displacementTileY * 1 << actualZoom)) * imageSize)/scale;
            normLon -= 0.5;
            normLon *= 360.0;
            normLat -= 0.5;
            normLat *= -2.0*Math.PI;
            normLat = Math.Exp(normLat);
            return;
            // first convert to Mercator projection
            // first convert the lat lon to mercator coordintes.
            //if (lon > 180)
            //{
            //    lon -= 360;
            //}

            //lon /= 360;
            //lon += 0.5;

            //lat = 0.5 - ((Math.Log(Math.Tan((Math.PI / 4) + (degree2Radian(0.5 * lat)))) / Math.PI) / 2.0);
        }
        private double[] dPix2dGeo(int dPixX, int dPixY, int zoom)
        {
            double salto_lat_tile = Math.Abs(ne.Latitude- sw.Latitude) / Math.Pow(2, zoom);
            double salto_long_tile = Math.Abs(ne.Longitude- sw.Longitude) / Math.Pow(2, zoom);

            double dGeoLat = ((double)(dPixY * salto_lat_tile)) / ((double)imageSize);
            double dGeoLong = ((double)(dPixX * salto_long_tile)) / ((double)imageSize);

            double[] res = new double[2];
            res[0] = dGeoLat;
            res[1] = dGeoLong;
            return res;
        }
        private double degree2Radian(double angle)
        {
            return angle * (Math.PI / 180);
        }
        private double radian2Degree(double rad)
        {
            return rad / (Math.PI / 180);
        }
        private double[] latlon2pix(double dLat, double dLong, int zoom){
            double lonD = dLong;
            if (lonD > 180)
            {
                lonD -= 360;
            }

            lonD /= 360;
            lonD += 0.5;

            double latD = 0.5 - ((Math.Log(Math.Tan((Math.PI / 4) + (0.5 * (dLat) * Math.PI / 180))) / Math.PI) / 2.0);
            double scale = (1 << zoom) * imageSize;
            double[] res = new double[2];
            res[0] = latD * scale;
            res[1] = lonD * scale;
            return res;           
        }
        private int[] dGeo2dPix(double dLat, double dLong, int zoom)
        {
            double salto_lat_tile = Math.Abs(ne.Latitude - sw.Latitude) / Math.Pow(2, zoom);
            double salto_lon_tile = Math.Abs(ne.Longitude - sw.Longitude) / Math.Pow(2, zoom);
            double actualLat = -(actualRow-1) * salto_lat_tile + ne.Latitude;
            double actualLon = (actualColumn-1) * salto_lon_tile + sw.Longitude;
            int dy = (int)(((double)-((dLat - actualLat))) / ((double)salto_lat_tile) * imageSize);
            int dx = (int)(((double)((dLong - actualLon))) / ((double)salto_lon_tile) * imageSize);
            int[] res = new int[2];
            res[0] = dy+offsetY;
            res[1] = dx+offsetX;
            return res;
        }
        public void center(LatLon coordenadas)
        {
            //Este metodo es un setCEnter.
            bool actualizaTiles = false;
            this.actualCenter = coordenadas;
            int[] tmoRowCol = toTileXY(coordenadas,actualZoom);
            int[] pXY=toZoomedPixelCoords(coordenadas,actualZoom);
            // Obtenemos el Tile que tiene esta coordenadas
            int[] filcol = geo2tile(coordenadas, actualZoom);
            centralTilePixel = new int[2] { (tmoRowCol[1] - 1) * imageSize, (tmoRowCol[0] - 1) * imageSize };
            //MessageBox.Show(coordenadas.ToString()+" zoom="+actualZoom);
            //MessageBox.Show("Offset actual x="+offsetX+" y=" + offsetY);
            //actualRow = filcol[0];
            //actualColumn = filcol[1];
            if (actualRow != tmoRowCol[0] || actualColumn != tmoRowCol[1])
                actualizaTiles = true;
            actualRow = tmoRowCol[0];
            actualColumn = tmoRowCol[1];
            this.LatHeigthLonWidth = tileLonWidthLatHeight(actualRow, actualColumn, actualZoom);
            LatLon latlongNE = getNE(actualRow, actualColumn, actualZoom);
            LatLon latlongSW = getSW(actualRow, actualColumn, actualZoom);

            LatLon tileCentralCoord = new LatLon();
            tileCentralCoord.Latitude = latlongNE.Latitude;
            tileCentralCoord.Longitude = latlongSW.Longitude;

            double pasoLat = Math.Abs(latlongNE.Latitude - latlongSW.Latitude);
            double pasoLong = Math.Abs(latlongNE.Longitude - latlongSW.Longitude);

            double deltaLat = Math.Abs(latlongNE.Latitude - coordenadas.Latitude);
            double deltaLong = Math.Abs(latlongSW.Longitude - coordenadas.Longitude);

            double latRatio = deltaLat / pasoLat;
            double longRatio = deltaLong / pasoLong;

            int deltaTileY = (int)(latRatio * imageSize);
            int deltaTileX = (int)(longRatio * imageSize);

            //offsetX = this.width / 2 - (tileCentral[0] - pXY[0]); // Latitud Y
            //offsetY = this.height / 2 - (tileCentral[1] - pXY[1]); // Longitud X
            offsetX = this.width / 2 + (centralTilePixel[0] - pXY[0]); // Latitud Y
            offsetY = this.height / 2 + (centralTilePixel[1] - pXY[1]); // Longitud X
            acumuladorX = (centralTilePixel[0] - pXY[0]);
            acumuladorY = (centralTilePixel[1] - pXY[1]);
            centralTilePixel[0] = pXY[0];
            centralTilePixel[1] = pXY[1];
            if (actualizaTiles)
                updateTilesMatrix();
            //refresh();

        }
        public LatLon cornerNW() 
        {
            return new LatLon(this.actualCenter.Latitude + this.LatHeigthLonWidth[0] * aroundTile, this.actualCenter.Longitude - this.LatHeigthLonWidth[1] * aroundTile);
           
        }
        public LatLon cornerSE() 
        {
            return new LatLon(this.actualCenter.Latitude - this.LatHeigthLonWidth[0] * aroundTile, this.actualCenter.Longitude + this.LatHeigthLonWidth[1] * aroundTile);
        }
        private void updateTilesMatrix()
        {
            LatLon cornerNW = new LatLon(this.actualCenter.Latitude + this.LatHeigthLonWidth[0] * aroundTile, this.actualCenter.Longitude - this.LatHeigthLonWidth[1] * aroundTile);
            LatLon cornerSE = new LatLon(this.actualCenter.Latitude - this.LatHeigthLonWidth[0] * aroundTile, this.actualCenter.Longitude + this.LatHeigthLonWidth[1] * aroundTile);
            foreach (Group group in groups)
            {

                if (group.Visible && this.actualZoom >= group.Zoom)
                {
                    group.update(cornerNW,cornerSE);
                }
            }
            for (int i = 0; i < nImages; i++)
            {
                for (int j = 0; j < nImages; j++)
                {
                    int tmpRow = actualRow + i - aroundTile;
                    int tmpColumn = actualColumn + j - aroundTile;
                    tiles[i, j] = cache.buscarTile(actualZoom, tmpRow, tmpColumn);
                }
            }
        }
        private int[] geo2tile(LatLon coordenada, int zoom)
        {
            /*Los saltos correponden al ancho y alto en coordenadas geograficas de un tile, 
             dado un nivel de zoom. Para un nivel de zoom z, por construccion el mapa base 
             obtenido de MapPoint Webservice esta formado por 2^z x 2^z tiles
             */
            double salto_lat_tile = Math.Abs(ne.Latitude - sw.Latitude) / Math.Pow(2, zoom);
            double salto_long_tile = Math.Abs(ne.Longitude - sw.Longitude) / Math.Pow(2, zoom);

            /*Console.WriteLine("pasos");
            Console.WriteLine("lat "+paso_lat_tile);
            Console.WriteLine("long "+paso_long_tile);*/

            /*distancia en coordenadas geográficas del punto a analizar con el punto elegido
             arbitrariamente como origen del sistema de tiles (que corresponde a la esquina
             superior izquierda del cuadrado que cubre gran parte del anillo américo vespucio, 
             ubicado en coordenadas latitud = -33.371344322087 y longitud = -70.7423288147549 */

            double delta_lat = ne.Latitude-coordenada.Latitude;
            double delta_long = sw.Longitude- coordenada.Longitude;

            /* Console.WriteLine("deltas");
             Console.WriteLine("delta lat " + delta_lat);
             Console.WriteLine("delta long " + delta_long);*/

            /*Ahora esta distancia en coordenadas geográficas la dividimos por el paso 
             correspondiente a este nivel de zoom, y se obtienes las coordenadas del tile
             que contiene al punto entregado como parametro a la funcion*/

            int ind_x, ind_y;
            double cuolat = delta_lat / salto_lat_tile;
            ind_y = (int)cuolat + 1;
            if (cuolat < 0)
            {
                ind_y = (int)cuolat;
            }
            double cuolong = delta_long / salto_long_tile;
            ind_x = -(int)cuolong + 1;
            if (cuolong > 0)
            {
                ind_x = -(int)cuolong;
            }
            int[] res = new int[2];
            res[0] = ind_y;
            res[1] = ind_x;
            return res;
        }
        public LatLon click2LatLon(int x,int y)
        {
            //Este metodo me da el punto de donde di click
            LatLon latlongNE;
            LatLon latlongSW;
            LatLon latlongNW;
            LatLon[] tmp= tileLatLon(actualColumn, actualRow, actualZoom);
            //latlongNE = getNE(actualRow, actualColumn, actualZoom);
            //latlongSW = getSW(actualRow, actualColumn, actualZoom);
            latlongNE = new LatLon(tmp[0].Latitude,tmp[1].Longitude);
            latlongSW = new LatLon(tmp[1].Latitude, tmp[0].Longitude);
            double[] dGeo;
            dGeo = dPix2dGeo(this.offsetX - x, this.offsetY - y, actualZoom);
            double deltaLat = (latlongNE.Latitude - latlongSW.Latitude) / this.imageSize * (this.offsetY - y);
            double deltaLon = (latlongNE.Longitude - latlongSW.Longitude) / this.imageSize * (this.offsetX - x);
            //dGeo = dPix2dGeo(this.offsetX - x, this.offsetY - y, actualZoom);
            latlongNW = new LatLon();
            latlongNW.Latitude = latlongNE.Latitude;
            latlongNW.Longitude = latlongSW.Longitude;

            //latlongNW.Latitude += dGeo[0];
            //latlongNW.Longitude -= dGeo[1];
            latlongNW.Latitude += deltaLat;
            latlongNW.Longitude -= deltaLon;

            return latlongNW;
        }
        private void zoomClick(object sender, EventArgs e)
        {
            if (this.dibujar)
            {
                this.poligono.borrarPoligonoEnEdicion();
                this.refresh();
            }
            else
            {
                if (this == null)
                    return;
                MouseEventArgs tmp = (MouseEventArgs)e;
                if (tmp.Button == MouseButtons.Left)
                {
                    if (this.actualZoom < this.zoomLevels)
                    {
                        this.move((this.width / 2 - tmp.X), (this.height / 2 - tmp.Y));
                        this.zoomIn();
                        this.move(-(this.width / 2 - tmp.X), -(this.height / 2 - tmp.Y));
                        //mapa.move((tmp.X  - imagenMapa.Width/2), (tmp.Y  - imagenMapa.Height/2));
                    }
                }
                else if (tmp.Button == MouseButtons.Right)
                {
                    if (this.actualZoom > 0)
                    {
                        this.move((this.width / 2 - tmp.X), (this.height / 2 - tmp.Y));
                        this.zoomOut();
                        this.move(-(this.width / 2 - tmp.X), -(this.height / 2 - tmp.Y));
                    }
                }
                this.refresh();
            }
        }
        private void updateZoom(object sender, EventArgs e)
        {
            
            if (this == null || this.actualZoom == this.zoomer.Value)
                return;
            if (this.zoomer.Value == 6)
            {
                FormPrincipal.Instance.cbImagenSatelital_Enable = true;
            }
            else 
            {
                FormPrincipal.Instance.cbImagenSatelital_Enable = false;
            }
            this.zoom(this.zoomer.Value);
            this.refresh();
        }
        private void mouseDown(object sender, MouseEventArgs e)
        {
            this.click2LatLon(e.X, e.Y);
            this.image.Controls.RemoveByKey("VentanaInfo");
            if (this == null)
                return;
            clickX = e.X;
            clickY = e.Y;
            if (FormPrincipal.Instance.DibujarDistancia)
            {
                
                if (!FormPrincipal.Instance.DibujarDistancia2)
                {
                    this.desplazamientoI.X = 0;
                    this.desplazamientoI.Y = 0;
                    p1.X = clickX;
                    p1.Y = clickY;
                    Pto1 = this.click2LatLon(p1.X, p1.Y);
                    FormPrincipal.Instance.DibujarDistancia2 = true;

                }
                else {
                    this.desplazamiento.X = 0;
                    this.desplazamiento.Y = 0;
                    p2.X = clickX;
                    p2.Y = clickY;
                    Pto2 = this.click2LatLon(p2.X, p2.Y);
                    FormPrincipal.Instance.DibujarDistancia2 = false;
                    this.refresh();
                }
            }

            if (this.dibujar)
            {
                LatLon punto = this.click2LatLon(e.X, e.Y);
                if (this.poligono.agregarPunto(punto, new[] { e.X, e.Y })) {
                    dibujar = false;    
                }
                this.refresh();
            }
            
        }
        private void mouseUp(object sender, MouseEventArgs e)
        {
            if (this == null)
                return;
            //     MessageBox.Show("Movi X" + (e.X - clickX) + " Y" + (e.Y - clickY));
            int diffX=e.X - clickX;
            int diffY=e.Y - clickY;
            if (diffX == 0 && diffY == 0)
                return;
            this.move(e.X - clickX, e.Y - clickY);
            this.refresh();
        }
        //Agregada esta funcion
        public void changeLayer(string layer)
        {
            foreach (Group g in groups)
            {
                string name = g.nombre;
                name = name.ToLower();
                if (name.IndexOf(layer) > -1)
                    g.Visible = !g.Visible;
            }
        }
        public int Zoom{
            get{
                return this.actualZoom;
            }
            set{
                this.zoom(value);
                //this.actualZoom = value;
            }
        }
        public Bitmap Image
        {
            get
            {
                return this.buffer;
            }
        }
        public Panel Panel
        {
            get
            {
                return this.composite;
            }
        }
        public LatLon Centro
        {
            get
            {
                return this.actualCenter;
            }
            set
            {
                this.center(value);
                this.refresh();
            }
        }
        //-------------------------------------------Aqui empece a programar----------------------------------
        public Boolean Dibujar
        {
            get
            {
                return this.dibujar;
            }
            set
            {
                    this.dibujar = value;
            }
        }
        public PictureBox Imagen
        {
            get
            {
                return this.image;
            }
        }
        public int getZoom
        {
            get
            {
                return this.zoomer.Value;
            }
        }
        public void nuevoEdificio(){
            poligono = new classEdificiosSantiago(pathData,this.actualZoom);
        }
        // private int[] centralTilePixel;
        public int[] CentralTilePixel
        {
            get
            {
                return this.centralTilePixel;
            }
        }
        public int getWidth
        {
            get
            {
                return this.width;
            }
        }
        public int getHeight
        {
            get
            {
                return this.height;
            }
        }
        public Panel Composite
        {
            get 
            {
                return this.composite;
            }
        }
        //actualZoom
        public int getActualZoom
        {
            get
            {
                return this.actualZoom;
            }
        }

        //private string pathData;
        public string PathData
        {
            get
            {
                return this.pathData;
            }
        }
        private void mouseMove(object sender, MouseEventArgs e)
        {

            //MessageBox.Show("up");
            if (FormPrincipal.Instance.DibujarDistancia2)
            {
                this.desplazamiento.X = 0;
                this.desplazamiento.Y = 0;
                p2.X = e.X;
                p2.Y = e.Y;
                Pto2 = this.click2LatLon(p2.X, p2.Y);
                this.refresh();
            }
        }
        public System.Drawing.Point P1
        {
            get
            {
                return this.p1;
            }
            set
            {
                this.p1 = value;
            }
        }
        public System.Drawing.Point P2
        {
            get
            {
                return this.p2;
            }
            set
            {
                this.p2 = value;
            }
        }
        public System.Drawing.Point Desplazamiento
        {
            get
            {
                return this.desplazamiento;
            }
            set
            {
                this.desplazamiento = value;
            }
        }
        public System.Drawing.Point DesplazamientoI
        {
            get
            {
                return this.desplazamientoI;
            }
            set
            {
                this.desplazamientoI = value;
            }
        }
        public LatLon PTO1
        {
            get
            {
                return this.Pto1;
            }
            set
            {
                this.Pto1 = value;
            }
        }
        public LatLon PTO2
        {
            get
            {
                return this.Pto2;
            }
            set
            {
                this.Pto2 = value;
            }
        }
        public Graphics Canvas
        {
            get 
            {
                return this.canvas;
            }
        }
        public LatLon NE
        {
            get
            {
                return this.ne;
            }
        }
        public LatLon SW
        {
            get
            {
                return this.sw;
            }
        }
        //-------------------------------------------Aldo empezo a programar-------------------------------------------

        public void AddPoint(string type, string name, string address, string latitude, string longitude, string description)
        {
            foreach (Group group in groups)
                if (group.Name.Equals(type))
                    group.AddPoint(name, address, latitude, longitude, description);
        }

        public string[] GetTypes()
        {
            string[] types = new string[groups.Count];
            int i = 0;

            foreach (Group group in groups)
            {
                types[i] = group.Name;
                ++i;
            }

            return types;
        }
        public delegate void setMeteoDataCallback(MeteoData dat);

        public void setMeteoData(MeteoData dat)
        {
            // FIXME
            /*
        private Label temp;
        private Label hum;
        private Label cond;
        private Label wind;
             */
            //Console.Write(dat.temperature);
            Map.metdata = dat;
            this.temp.Text = ("Temperatura: " + Map.metdata.Temperature + "°C");
            this.hum.Text = (Map.metdata.Humidity);
            this.cond.Text = ("Condición: " + Map.metdata.Current);
            this.wind.Text = (Map.metdata.Wind);
        }
        // ---------------------- Aldo empezó a programar -------------------

        public void selectDest(double x, double y)
        {
            visible = true;
            dest = new LatLon();

            dest.Latitude = x;
            dest.Longitude = y;

            Centro.Latitude = x;
            Centro.Longitude = y;

            center(Centro);
            this.refresh();
        }

        public delegate void printPointCallback(double lat, double lon);

        public void printPoint(double x, double y)
        {
            this.pos = new LatLon(x, y);

            //this.pos.Latitude = x;
            //this.pos.Longitude = y;
            this.setteado = true;

            //Antes de hacer refresh, calcular los puntos más cercanos
            //this.nearPoints();

            this.refresh();
        }

        private void dibujarPosicion(LatLon dest)
        {
            Bitmap actualIcon = new Bitmap(pathData+"\\Icons\\Actual4.png");
            PointOfInterest.Desktop.Point point = new PointOfInterest.Desktop.Point(dest, "Posicion actual", " ", actualIcon);
            point.Coordenadas = dest;

            int[] position = this.toZoomedPixelCoords(point.Coordenadas, this.actualZoom);

            point.actualPixelX = position[0];
            point.actualPixelY = position[1];
            point.actualZoom = actualZoom;
            int positionScreenX = this.width / 2 + (point.actualPixelX - centralTilePixel[0]) - 16;
            int positionScreenY = this.height / 2 + (point.actualPixelY - centralTilePixel[1]) - 16;
            point.Location = new System.Drawing.Point(positionScreenX, positionScreenY);
            point.BringToFront();
            this.image.Controls.Add(point);
            //this.canvas.DrawImage(actualIcon, positionScreenX, positionScreenY);
        }

        private void dibujarResaltado(LatLon pto, Bitmap icon)
        {
            PointOfInterest.Desktop.Point point = new PointOfInterest.Desktop.Point(dest, "Posicion actual", " ", icon);
            point.Coordenadas = pto; //TE PILLE, BUG QLIAO!

            int[] position = this.toZoomedPixelCoords(point.Coordenadas, this.actualZoom);

            point.actualPixelX = position[0];
            point.actualPixelY = position[1];
            point.actualZoom = actualZoom;
            int positionScreenX = this.width / 2 + (point.actualPixelX - centralTilePixel[0]) - 16;
            int positionScreenY = this.height / 2 + (point.actualPixelY - centralTilePixel[1]) - 16;
            point.Location = new System.Drawing.Point(positionScreenX, positionScreenY);
            point.BringToFront();
            this.image.Controls.Add(point);
            //canvas.DrawImage(icon, positionScreenX, positionScreenY);
        }

        private void dibujarDest(LatLon dest)
        {
            if (!visible) return;

            Bitmap actualIcon = new Bitmap(pathData+"\\Icons\\Actual4.png");
            PointOfInterest.Desktop.Point point = new PointOfInterest.Desktop.Point(dest, "Posicion actual", " ", actualIcon);
            point.Coordenadas = dest;

            int[] position = this.toZoomedPixelCoords(point.Coordenadas, this.actualZoom);

            point.actualPixelX = position[0];
            point.actualPixelY = position[1];
            point.actualZoom = actualZoom;
            int positionScreenX = this.width / 2 + (point.actualPixelX - centralTilePixel[0]) - 16;
            int positionScreenY = this.height / 2 + (point.actualPixelY - centralTilePixel[1]) - 16;
            point.Location = new System.Drawing.Point(positionScreenX, positionScreenY);
            point.BringToFront();
            this.image.Controls.Add(point);
            canvas.DrawImage(actualIcon, positionScreenX, positionScreenY);
        }
        public void borrarDest()
        {
            this.visible = false;
            this.refresh();
        }
        public Boolean varImagenSatelital
        {
            get 
            {
                return this.ImagenSatelital;
            }
            set 
            {
                this.ImagenSatelital = value;
            }
        }
        public void nearPoints()
        {
            // Iterar sobre todos los grupos
            foreach (Group g in groups)
            {
                if (g.Name.ToLower().Contains("grifo"))
                {
                    // Revisar todos los grifos
                    // Se Asocia cada Punto con su distancia a la posición
                    Dictionary<PointOfInterest.Desktop.Point, double> map = new Dictionary<PointOfInterest.Desktop.Point, double>();
                    foreach (PointOfInterest.Desktop.Point p in g.visiblePoints)
                    {
                        map.Add(p, Distancia.getDistance(p.Coordenadas, this.pos));
                    }
                    // Ordenar los elementos por distancia
                    List<KeyValuePair<PointOfInterest.Desktop.Point, double>> lista = new List<KeyValuePair<PointOfInterest.Desktop.Point, double>>(map);
                    lista.Sort((primerPar, sgtePar) =>
                    {
                        return primerPar.Value.CompareTo(sgtePar.Value);
                    }
                    );

                    // Recorrer la lista que ya está ordenada
                    int count = 0;

                    foreach (KeyValuePair<PointOfInterest.Desktop.Point, double> p in lista)
                    {
                        // Cambiar el ícono del grifo
                        p.Key.Resaltado = true;
                        count++;
                        if (count >= Map.grifCerc)
                            break;
                    }
                }
                if (g.Name.ToLower().Contains("carabineros"))
                {
                    // Revisar todas las comisarías
                    foreach (PointOfInterest.Desktop.Point p in g.visiblePoints)
                    {
                    }
                }
                if (g.Name.ToLower().Contains("hospital"))
                {
                    // Revisar todos los hospitales
                    foreach (PointOfInterest.Desktop.Point p in g.visiblePoints)
                    {
                    }
                }
            }
        }
    }
}
